package vue.panels;

import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferedImage;
import java.util.Iterator;
import java.util.Random;
import java.util.Vector;

import vue.Ui;
import vue.components.*;

import controler.Case;
import controler.Joueur;
import controler.Protocol;
import controler.ProtocolException;
import controler.Table;
/**
 * <p>Title: vue.Plateau </p>
 *
 * <p>
 * 	Description: Cette classe gere l'affichage de la classe du plateau de jeu
 * </p>
 *
 * <p>Copyright: (c) 2008 FSSIM</p>
 *
 * <p>Company: FSSImock</p>
 *
 * @author Antoine Farnault
 * @author Augustin Couette
 * @version 1.0
 */
public class Plateau extends TPPanel implements MouseListener,MouseMotionListener {

	private static final long serialVersionUID = 1L;
	//boucle d'affichage
	Affichage affich;
	private int idSurvole = -1;
	// buffer memoire ou les images et les textes sont appliques (double buffering)
	Graphics2D buffer;

	// image affiche au final
	public BufferedImage image;
	
	public controler.Plateau p;
	
	final int CENTRE_X       = 10;
	final int CENTRE_Y		 = 0;
	final int PLATEAU_WIDTH  = 480;
	final int PLATEAU_HEIGHT = 480;
	public final int CASE_WIDTH     = 30;
	public final int DIAMETRE       = PLATEAU_WIDTH - 25;
	final int RAYON_CIRCLE   = DIAMETRE / 2;
	final int CASE_HEIGHT    = RAYON_CIRCLE/9;
	final int TAILLE_PION    = 18;
	public Table t; //on est sur cette table
	public Vector<ListItem> pions = new Vector<ListItem>();
	public Vector<Case> casesAccessibles = new Vector<Case>();
	
	Dimension dim = new Dimension(PLATEAU_WIDTH, PLATEAU_HEIGHT);
	
	// ++ les coordonnees de la souris
	int MOUSE_X;
	int MOUSE_Y;
	// -- les coordonnees de la souris
	
	// ++ les couleurs par defaut
	private final Color COLOR_FOND	= new Color(255,255,255);
	private final Color COLOR_LIGNE	= Color.black;
	// -- les couleurs par defaut
	
	/**
	 * Constructeur de plateau
	 */
	public Plateau(Table t) {
		this.t = t;
		affich = new Affichage();
		p = new controler.Plateau(this);
		addMouseMotionListener(this);
		tracerCasesLogique();
		initComponents();
		affich.start();
	}
	
	/**
	 * initialisation des composants graphiques
	 * @see #Plateau(Ui)
	 */
	public void initComponents() {
		setName("plateauPanel");
		addMouseListener(this);
	}
	
	/**
	 * Methode de mis a jour du graphique
	 */
	public void update(Graphics g) {
		paint(g);
	}

	
	
	/**
	 * methode d'affichage du graphique 
	 */
	public void paint(Graphics g) {
		if(buffer == null){
			image = (BufferedImage)createImage(dim.width,dim.height);
			buffer = (Graphics2D)image.getGraphics();
		}
		
		//buffer.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.8f));
        buffer.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
	    buffer.setPaint(COLOR_FOND);
       	buffer.fill(new Rectangle(0,0,dim.width+600,dim.height+600));
    	
		tracerCases();
		tracerPionsJoueurs();
		tracerCasesAccessible();
		g.drawImage(image,0,0,this);
	}

	private void tracerCasesAccessible() {
		Iterator<Case> it = casesAccessibles.iterator();
		while(it.hasNext()){
			Case tmp = it.next();
			tmp.couleur = Color.black; // FIXME
			drawCase(tmp);
		}
	}

	/**
	 * Methode de tracage du plateau de jeu.
	 * Dessine les cases que l'on peut jouer. 
	 */
	public void tracerCases(){
		int idCase = 0;
		
		buffer.setPaint(COLOR_LIGNE);
		buffer.setStroke(new BasicStroke(1.0f));
		
		
		double alpha = (2*Math.PI/45); // angle de separation entre 2 cases
		int alphaDegree = 8;
		double beta  = (18*Math.PI/270); // pour les plus grosses cases
		int betaDegree = 12;
		double angle = 0;
		int angleDegree = 270;
		int startAngle = 270;
		
		for(int i=0; i<6; i++){
			for(int j=0; j<6; j++){
				double x1 =  RAYON_CIRCLE * Math.sin(angle);
				double y1 =	RAYON_CIRCLE * Math.cos(angle);
				double x2 =  (RAYON_CIRCLE - CASE_WIDTH) * Math.sin(angle);
				double y2 =	(RAYON_CIRCLE - CASE_WIDTH) * Math.cos(angle);
				x1 = x1 + RAYON_CIRCLE + CENTRE_X;
				y1 = y1 + RAYON_CIRCLE + CENTRE_Y;
				x2 = x2 + RAYON_CIRCLE + CENTRE_X;
				y2 = y2 + RAYON_CIRCLE + CENTRE_Y;
				
				idCase ++;
				
				
				
				drawCase(p.cases.get(idCase));
				
				buffer.setPaint(COLOR_LIGNE);
				buffer.drawLine((int)x1, (int)y1, (int)x2, (int)y2);
				angle = angle +alpha;
				angleDegree += alphaDegree;
			}
			buffer.setPaint(COLOR_FOND);
			buffer.fillArc((int)CENTRE_X + CASE_WIDTH, (int)CENTRE_Y + CASE_WIDTH, DIAMETRE- 2*CASE_WIDTH, DIAMETRE- 2*CASE_WIDTH, startAngle, 6*alphaDegree);
			startAngle += (6*alphaDegree + betaDegree);

			
			// ++ tracage des grosses cases
			double x1 =  RAYON_CIRCLE * Math.sin(angle);
			double y1 =	RAYON_CIRCLE * Math.cos(angle);
			double x2 =  (RAYON_CIRCLE - CASE_WIDTH) * Math.sin(angle);
			double y2 =	(RAYON_CIRCLE - CASE_WIDTH) * Math.cos(angle);
			x1 = x1 + RAYON_CIRCLE + CENTRE_X;
			y1 = y1 + RAYON_CIRCLE + CENTRE_Y;
			x2 = x2 + RAYON_CIRCLE + CENTRE_X;
			y2 = y2 + RAYON_CIRCLE + CENTRE_Y;
			
			idCase++;			
			buffer.setPaint(COLOR_LIGNE);
			buffer.drawLine((int)x1, (int)y1, (int)x2, (int)y2);
			drawCase(p.cases.get(idCase));
			
			// -- tracage des grosses cases

			// On retient pour dessiner le polygon final
			double x3Tmp=0;
			double y3Tmp=0;
			double x4Tmp=0;
			double y4Tmp=0;
			
			 // Pour enlever les impuretes
			//buffer.setPaint(COLOR_FOND);
			//buffer.fillOval(CENTRE_X + CASE_WIDTH , CENTRE_Y + CASE_WIDTH, DIAMETRE - 2*CASE_WIDTH, DIAMETRE-2*CASE_WIDTH);
			
			int rayon = RAYON_CIRCLE - CASE_WIDTH;
			for(int k=0;k<6;k++){ // chaque diagonale et perpendiculaire
				double x3 =  rayon * Math.sin(angle);
				double y3 =	rayon * Math.cos(angle);
				double x4 =  rayon * Math.sin(angle+beta);
				double y4 =	 rayon * Math.cos(angle+beta);
				x3 = x3 + RAYON_CIRCLE + CENTRE_X;
				y3 = y3 + RAYON_CIRCLE + CENTRE_Y;
				x4 = x4 + RAYON_CIRCLE + CENTRE_X;
				y4 = y4 + RAYON_CIRCLE + CENTRE_Y;

				rayon = rayon - CASE_HEIGHT;
				//if (k!=5){
				//	idCase ++;
				//}
				if (k!=0 && k!=5){ //k!=0 pour des raisons esthetiques.
					buffer.setPaint(COLOR_LIGNE);
					buffer.drawLine((int)x3, (int)y3, (int)x4, (int)y4);
					idCase ++;
				}
				if(k!=0){
					drawCase(p.cases.get(idCase));
				}
				if(k==0){
					x3Tmp = x3;
					y3Tmp = y3;
					x4Tmp = x4;
					y4Tmp = y4;
				}
				if (k==5){
					buffer.setPaint(COLOR_LIGNE);
					buffer.drawLine((int)x3Tmp,(int)y3Tmp,(int)x3,(int)y3);
					buffer.drawLine((int)x4Tmp,(int)y4Tmp,(int)x4,(int)y4);
				}
			}
			angle = angle + beta;
			angleDegree += betaDegree; 
		}
		//buffer.drawPolygon(xPoints, yPoints, 12); // dessin du polygon central
		
		// on trace les 2 cercles principaux
		buffer.setPaint(COLOR_LIGNE);
		buffer.drawOval(CENTRE_X, CENTRE_Y , DIAMETRE, DIAMETRE);
		buffer.drawOval(CENTRE_X + CASE_WIDTH , CENTRE_Y + CASE_WIDTH, DIAMETRE - 2*CASE_WIDTH, DIAMETRE-2*CASE_WIDTH);
		
		buffer.setPaint(COLOR_FOND);
		buffer.fillOval(CENTRE_X+5*CASE_WIDTH, CENTRE_Y+5*CASE_WIDTH, DIAMETRE-10*CASE_WIDTH, DIAMETRE-10*CASE_WIDTH);
		buffer.setPaint(COLOR_LIGNE);
		buffer.drawOval(CENTRE_X+5*CASE_WIDTH, CENTRE_Y+5*CASE_WIDTH, DIAMETRE-10*CASE_WIDTH, DIAMETRE-10*CASE_WIDTH);
	}
	
	/**
	 * Tracage de la case c
	 * @param b
	 * @param c
	 */
	private void drawCase(Case c){
		buffer.setPaint(c.couleur);
		buffer.fillArc(c.x, c.y, c.diametre, c.diametre, c.startAngle, c.angleWidth);
		if(c.id == idSurvole){
			drawPion(c, Joueur.couleur);
		}
	}
	
	private void drawPion(int id, Color couleur){
		drawPion(p.cases.get(id), couleur);
	}
	
	private void tracerPionsJoueurs() {
		Iterator<ListItem> it = pions.iterator();
		while(it.hasNext()){
			ListItem tmp = it.next();
			drawPion(Integer.parseInt(tmp.value), tmp.color);
		}
	}
	
	private void drawPion(Case c, Color couleur) {
		if(c.id == 0){
			int x,y;
			x = RAYON_CIRCLE+CENTRE_X - TAILLE_PION/2;
			y = RAYON_CIRCLE+CENTRE_Y - TAILLE_PION/2;
			/*Random r = new Random();
			x += r.nextInt(DIAMETRE-14*CASE_WIDTH);
			y += r.nextInt(DIAMETRE-14*CASE_WIDTH);*/
			buffer.setPaint(couleur);
			buffer.fillOval(x, y, TAILLE_PION, TAILLE_PION);
			
			buffer.setStroke(new BasicStroke(2.0f));
			buffer.setPaint(COLOR_LIGNE);
			buffer.drawOval(x,y, TAILLE_PION, TAILLE_PION);
		}else{
			Point p = getCoord((c.a+c.b)/2, (c.diametre-c.h)/2, TAILLE_PION);
			buffer.setPaint(couleur);
			buffer.fillOval(p.x, p.y, TAILLE_PION, TAILLE_PION);
			
			buffer.setStroke(new BasicStroke(2.0f));
			buffer.setPaint(COLOR_LIGNE);
			buffer.drawOval(p.x, p.y, TAILLE_PION, TAILLE_PION);
		}
		buffer.setStroke(new BasicStroke(1.0f));
	}

	public Point getCoord(double angle, double d, int taille){
		Point p = new Point();
		p.x = (int)(Math.sin(angle)*d -(taille/2)) + RAYON_CIRCLE + CENTRE_X;
		p.y = (int)(Math.cos(angle)*d -(taille/2)) + RAYON_CIRCLE + CENTRE_Y;
		return p;
	}
	
	/**
	 * Creer les nouvelles cases pour le controleur
	 */
	public void tracerCasesLogique(){
		
		double alpha = (2*Math.PI/45); // angle de separation entre 2 cases
		double beta  = (18*Math.PI/270); // pour les plus grosses cases
		double angle = 0;
		int angleDegree = 270;
		int betaDegree = 12;
		int alphaDegree = 8;
		int startAngle = 270;
		Vector<Color> couleurs=  new Vector<Color>();
		couleurs.add(Color.blue);
		couleurs.add(Color.green);
		couleurs.add(Color.orange);
		couleurs.add(Color.pink);
		couleurs.add(Color.white);
		couleurs.add(Color.yellow);
		
		
		int idCase = 0;
		Case c;
		c = new Case(idCase++, 0, Math.PI*2, 0, DIAMETRE-12*CASE_WIDTH, false, Color.white); 
		p.cases.add(c);
		for(int i=0; i<6; i++){
			for(int j=0; j<6; j++){
				c = new Case(idCase++,angle, angle+alpha, RAYON_CIRCLE-CASE_WIDTH, CASE_WIDTH,false,couleurs.get(j)); 
				c.x = CENTRE_X;
				c.y = CENTRE_Y;
				c.startAngle = angleDegree;
				c.angleWidth = alphaDegree;
				c.diametre = DIAMETRE;
				p.cases.add(c);
				angle = angle +alpha;
				angleDegree += alphaDegree;
			}
			c = new Case(idCase++,angle, angle+beta, RAYON_CIRCLE-CASE_WIDTH, CASE_WIDTH,true,couleurs.get(i)); 
			c.x = CENTRE_X;
			c.y = CENTRE_Y;
			c.startAngle = angleDegree;
			c.angleWidth = betaDegree;
			c.diametre = DIAMETRE;
			c.pions.add("ss");
			p.cases.add(c);
			startAngle += (6*alphaDegree + betaDegree);
			
			int rayon = RAYON_CIRCLE - CASE_WIDTH;
			
			for(int k=0;k<6;k++){ // chaque diagonale et perpendiculaire
				rayon = rayon - CASE_HEIGHT;
				if (k!=5 && k!=0){
					c = new Case(idCase++,angle, angle+beta, rayon, CASE_HEIGHT,false,couleurs.get((k+i)%6));
					c.x = CENTRE_X + k*CASE_WIDTH ;
					c.y = CENTRE_Y + k*CASE_WIDTH;
					c.diametre = DIAMETRE- k*2*CASE_WIDTH;
					c.startAngle = angleDegree;
					c.angleWidth = betaDegree;
					p.cases.add(c);
				}
			}
			angle = angle + beta;
			angleDegree += betaDegree; 
		}
	}
	
	/**
	 * Methode getAngle permet de trouver l'angle par rapport aux coordonnees du clique de souris
	 * @return double angle
	 */
	public double getAngle(){
		int x = MOUSE_X;
		int y = MOUSE_Y;
		x = x - CENTRE_X - RAYON_CIRCLE;
		y = y - CENTRE_Y - RAYON_CIRCLE;

		double angle = 0;
		double cos = (double)x/RAYON_CIRCLE;
		double sin = (double)y/RAYON_CIRCLE;
		   
		if (cos <= 0 && sin <=0){
			angle = (Math.PI*3/2 - Math.atan((double)(sin/cos)));
		}
		if (cos > 0 && sin <= 0){
			angle = (Math.PI/2 - Math.atan((double)(sin/cos)));
		}
		if (cos <=0 && sin >0 ){
			angle = (Math.PI*3/2 - Math.atan((double)(sin/cos)));
		}
		if (cos >= 0 && sin > 0 ){
			angle = (Math.PI/2 - Math.atan((double)(sin/cos)));
		}
			//return (angle+Math.PI) % (Math.PI*2);
			return angle;
	}
	
	
	
	/**
	 * Methode calculant la distance entre l'origine du plateau et la souris
	 */
	public double getDistance(){
		int x = MOUSE_X;
		int y = MOUSE_Y;
		x = x - CENTRE_X - RAYON_CIRCLE;
		y = y - CENTRE_Y - RAYON_CIRCLE;
		return Math.sqrt(x*x + y*y);
	}
	
	/**
	 * Methode mouseClicked. Appelé a chaque clique de souris sur le canvas. 
	 */
	public void mouseClicked(MouseEvent evt) {
		if(!Joueur.de)
			return;
		Joueur.de = false;
		MOUSE_X = evt.getX();
		MOUSE_Y = evt.getY();
		double angle = getAngle();
		double distance = getDistance();
		Case c = p.getCase(angle, distance);
		try{
			System.out.println(c.id);
		}catch(Exception e){e.printStackTrace();}
		if (c == null)
			return;
		try {
			Protocol.snd_case(c.id, t);
			Joueur.caseRepondue = c;
		} catch (ProtocolException e) {
			e.printStackTrace();
		}
	}

	@Override
	public void mouseEntered(MouseEvent arg0) {}

	@Override
	public void mouseExited(MouseEvent arg0) {}

	@Override
	public void mousePressed(MouseEvent arg0) {}

	@Override
	public void mouseReleased(MouseEvent arg0) {}

	/**
	 * Classe interne d'animation.
	 * L'animation est gere dans un thread a part.  
	 */
	class Affichage extends Thread {
		/**
		 *  Ce thread appelle le rafraichissement de notre fenêtre 
		 *  toutes les 10 milli-secondes
		 */
		public void run(){
			while( true ){
				try {
					repaint(); // repaint appelle update() qui appelle paint()
					sleep( 100 );
				} catch ( Exception e ) {
					System.out.println("Exception methode du thread d'affichage: "+e.getMessage());        
				}
			}
		}
	}

	@Override
	public void mouseDragged(MouseEvent arg0) {}

	
	public void mouseMoved(MouseEvent evt) {
		MOUSE_X = evt.getX();
		MOUSE_Y = evt.getY();
		double angle = getAngle();
		double distance = getDistance();
		Case c = p.getCase(angle, distance);
		if(c!=null){
			this.idSurvole = c.id;
		}
		else
			this.idSurvole = -1;
	}
}
